package com.fyb.doublecache.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * @Author：MR.FANG
 * @name：CacheConfig
 * @Date：2025/7/3 上午10:02
 */
@Configuration
@EnableCaching
public class CacheConfig {

    /**
     * 配置Caffeine本地缓存管理器
     * Caffeine是一个高性能的Java本地缓存库，具有以下特点：
     * 1. 基于内存的高性能缓存
     * 2. 支持自动过期策略
     * 3. 支持基于容量的驱逐策略
     * 4. 支持统计功能
     *
     * 配置说明：
     * - initialCapacity: 初始容量，避免频繁扩容
     * - maximumSize: 最大容量，超过后会触发淘汰机制
     * - expireAfterWrite: 写入后多久过期，防止数据过期
     * - recordStats: 开启统计，方便监控缓存使用情况
     *
     * @return CaffeineCacheManager实例
     */
    @Bean
    public CaffeineCacheManager caffineCacheManager(){
        CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        caffeineCacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(1000) //初始容量，
                .maximumSize(10_000) //最大容量，超过后会触发淘汰机制
                .expireAfterWrite(Duration.ofMinutes(30)) // 写入30分钟后过期，防止数据过期
                .recordStats() // 开启统计，方便监控缓存使用情况
        );
        return caffeineCacheManager;
    }

    /**
     * 配置Redis分布式缓存管理器
     * Redis作为分布式缓存，具有以下特点：
     * 1. 支持分布式部署
     * 2. 数据持久化
     * 3. 支持多种数据结构
     * 4. 支持事务
     *
     * 配置说明：
     * - entryTtl: 缓存过期时间，防止数据过期
     * - serializeKeysWith: key的序列化方式，使用StringRedisSerializer
     * - serializeValuesWith: value的序列化方式，使用GenericJackson2JsonRedisSerializer
     * - disableCachingNullValues: 不缓存null值，避免缓存穿透
     *
     * @param connectionFactory Redis连接工厂，用于创建Redis连接
     * @return RedisCacheManager实例
     */
    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(60))  // 缓存过期时间60分钟
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))  // key序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))  // value序列化方式
                .disableCachingNullValues();  // 不缓存null值，避免缓存穿透

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()  // 支持事务，保证数据一致性
                .build();
    }

    /**
     * 配置双层缓存管理器
     * 将Caffeine和Redis缓存管理器组合成一个统一的缓存管理器
     *
     * 工作原理：
     * 1. 优先使用本地缓存(Caffeine)
     * 2. 本地缓存未命中时，查询Redis缓存
     * 3. Redis缓存命中时，同步到本地缓存
     * 4. 两级缓存都未命中时，从数据库加载
     *
     * @param caffeineCacheManager Caffeine缓存管理器，用于本地缓存
     * @param redisCacheManager Redis缓存管理器，用于分布式缓存
     * @return LayeredCacheManager实例
     */
    @Bean
    @Primary// 标记为主要缓存管理器，Spring会优先使用这个缓存管理器
    public LayeredCacheManager layeredCacheManager(CaffeineCacheManager caffeineCacheManager, RedisCacheManager redisCacheManager){
        return new LayeredCacheManager(caffeineCacheManager,redisCacheManager);
    }
}
